use apikit_expand::api_attribute::get_handle_def_ident;
use proc_macro2::TokenStream;
use quote::{ToTokens, format_ident, quote};
use syn::{FnArg, ImplItem, Item, Result, Type};

pub fn impl_api_expand(item: Item) -> Result<TokenStream> {
    let Item::Impl(item) = item else {
        return Err(moss::error::Error::custom("not supported item").into());
    };
    let Some((_, trait_ident, _)) = &item.trait_ else {
        return Err(moss::error::Error::custom("miss trait ident").into());
    };
    let trait_name = trait_ident.to_token_stream().to_string();
    let mut expanded = quote!();

    let self_ty = &item.self_ty;
    for fns in &item.items {
        let ImplItem::Fn(fn_item) = fns else {
            return Err(moss::error::Error::custom("not supported impl item").into());
        };
        let call_fn = &fn_item.sig.ident;
        let fn_ident_str = call_fn.to_token_stream().to_string();
        let mut input_sig = quote!();
        let mut formal_params = quote!();
        let mut informal_params = quote!();
        for (index, arg) in fn_item.sig.inputs.iter().enumerate() {
            let FnArg::Typed(pat_ty) = arg else { continue };
            let ty = &pat_ty.ty;
            let param_ident = format_ident!("param{}", index);
            input_sig = quote! {
                #input_sig #param_ident: __ConsolExtractor<#ty>,
            };
            formal_params = quote! {
                #formal_params <__ConsolExtractor<#ty> as carpet::server::handler::ConsolExtractor<#ty>>::to_extractor(#param_ident),
            };
            informal_params = quote! {
                #informal_params #param_ident,
            }
        }
        let handle_def = get_handle_def_ident(trait_name.as_str(), fn_ident_str.as_str());

        let impl_fn_ident =
            format_ident!("__axum_mr_{}_{}", trait_name.to_lowercase(), fn_ident_str,);
        let collect_fn_ident =
            format_ident!("__collect_{}_{}", trait_name.to_lowercase(), fn_ident_str,);

        let mut log_token = quote!();
        let return_ty = match &fn_item.sig.output {
            syn::ReturnType::Default => quote!(()),
            syn::ReturnType::Type(_, ty) => {
                gen_log_token(&mut log_token, ty);
                quote!(#ty)
            }
        };
        let impl_fn_block = quote! {
            async fn #impl_fn_ident(handler: #self_ty, #input_sig) -> axum::response::Response {
                let (#informal_params): <#handle_def as apikit::HandlerDef>::Input = (#formal_params);
                let result: <#handle_def as apikit::HandlerDef>::Output = <#self_ty as #trait_ident> :: #call_fn(handler, #informal_params).await;
                let mut error_msg: Option<String> = None;
                #log_token
                let response = <__ConsolResponse as carpet::server::handler::ConsolResponse<<#handle_def as apikit::HandlerDef>::Protocol, #return_ty>>::to_response(result);
                if let Some(error_msg) = error_msg {
                    if response.status().as_u16() >= 500 {
                        error!("{error_msg:?}");
                    }
                }
                response
            }

            fn #collect_fn_ident() -> carpet::server::handler::PathHandler<__AppState> {
                <#handle_def as carpet::server::handler::IntoPathHandler>::into_path_handler(#impl_fn_ident)
            }

            inventory::submit! {__MethodHandlerStrawImpl::new(#collect_fn_ident)}
        };
        expanded = quote! {
            #expanded
            #impl_fn_block
        }
    }
    expanded = quote! {
        #item

        #expanded
    };
    Ok(expanded)
}

pub fn impl_handler_expand(item: Item) -> Result<TokenStream> {
    let Item::Impl(item) = item else {
        return Err(moss::error::Error::custom("not supported item").into());
    };
    if item.trait_.is_some() {
        return Err(moss::error::Error::custom("trait not surport").into());
    }

    let trait_name = String::default();
    let mut expanded = quote!();

    let self_ty = &item.self_ty;
    for fns in &item.items {
        let ImplItem::Fn(fn_item) = fns else {
            return Err(moss::error::Error::custom("not supported impl item").into());
        };
        let call_fn = &fn_item.sig.ident;
        let fn_ident_str = call_fn.to_string();

        let mut input_sig = quote!();
        let mut formal_params = quote!();
        let mut informal_params = quote!();
        for (index, arg) in fn_item.sig.inputs.iter().enumerate() {
            let FnArg::Typed(pat_ty) = arg else { continue };
            let ty = &pat_ty.ty;
            let param_ident = format_ident!("param{}", index);
            input_sig = quote! {
                #input_sig #param_ident: __ConsolExtractor<#ty>,
            };
            formal_params = quote! {
                #formal_params <__ConsolExtractor<#ty> as carpet::server::handler::ConsolExtractor<#ty>>::to_extractor(#param_ident),
            };
            informal_params = quote! {
                #informal_params #param_ident,
            }
        }
        let def = get_handle_def_ident("", fn_ident_str.as_str());

        let impl_fn_ident =
            format_ident!("__axum_mr_{}_{}", trait_name.to_lowercase(), fn_ident_str,);
        let collect_fn_ident =
            format_ident!("__collect_{}_{}", trait_name.to_lowercase(), fn_ident_str,);

        let mut log_token = quote!();
        let return_ty = match &fn_item.sig.output {
            syn::ReturnType::Default => quote!(()),
            syn::ReturnType::Type(_, ty) => {
                gen_log_token(&mut log_token, ty);
                quote!(#ty)
            }
        };
        let impl_fn_block = quote! {
            async fn #impl_fn_ident(handler: #self_ty, #input_sig) -> axum::response::Response {
                let (#informal_params): <#def as apikit::HandlerDef>::Input = (#formal_params);
                let result: <#def as apikit::HandlerDef>::Output = handler.#call_fn(#informal_params).await;
                let mut error_msg: Option<String> = None;
                #log_token
                let response = <__ConsolResponse as carpet::server::handler::ConsolResponse<<#def as apikit::HandlerDef>::Protocol, #return_ty>>::to_response(result);
                if let Some(error_msg) = error_msg {
                    if response.status().as_u16() >= 500 {
                        error!("{error_msg:?}");
                    }
                }
                response
            }

            fn #collect_fn_ident() -> carpet::server::handler::PathHandler<__AppState> {
                <#def as carpet::server::handler::IntoPathHandler>::into_path_handler(#impl_fn_ident)
            }

            inventory::submit! {__MethodHandlerStrawImpl::new(#collect_fn_ident)}
        };
        expanded = quote! {
            #expanded
            #impl_fn_block
        }
    }
    expanded = quote! {
        #item

        #expanded

    };
    Ok(expanded)
}

fn gen_log_token(log_token: &mut TokenStream, ty: &Type) {
    if let Type::Path(path) = ty {
        if let Some(first) = path.path.segments.first() {
            if first.ident.to_string().eq("Result") {
                *log_token = quote! {
                    if let Err(e) = &result {
                        error_msg = Some(format!("处理器错误：{e:?}"));
                    }
                };
            }
        }
    }
}
